﻿using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Windows.Forms;

using ScintillaNet;
using WeifenLuo.WinFormsUI.Docking;
using PSTaskDialog;
using System.Runtime.Remoting.Messaging;

namespace ArduinoSketch.Core
{
    public delegate void OpenFileAsyncMethod(string fileName);

    public enum NewFileType : int
    {
        HEADER_FILE = 0,
        SOURCE_FILE
    }

    public class Sketch
    {
        private DockPanel _dockPanel;

        private ISolutionExplorer _explorer;
        private IOutputConsole _console;
        private System.Windows.Forms.Form _parent;

        #region
        private OpenFileDialog _openFileDialog = new OpenFileDialog();
        private SaveFileDialog _saveFileDialog = new SaveFileDialog();
        private FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
        #endregion

        private string _crrentUploadPort = String.Empty;
        private int _zoomLevel;
        public int _newDocumentCount = 0;
        private SolutionEntity _curSolution = null;
        private ProjectEntity _curProject = null;
        private Compiler _compiler = null;

        #region Async Method
        private OpenFileAsyncMethod _openFileAsyncMethod; 
        private AsyncCallback _compileCallback;
        private CompileProjectAsyncMethod _compileProjectAsyncMethod;
        private AsyncCallback _uploadCallback;
        private UploadAsyncMethod _uploadAsyncMethod;
        #endregion

        #region Event Handler
        public event EventHandler OnBeforCompile;
        public event EventHandler OnAfterCompile;
        public event EventHandler OnBeforUpload;
        public event EventHandler OnAfterUpload;
        public event EventHandler<ProjectSelectedArgs> OnProjectSelected;
        #endregion

        #region Constructor
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Parent"></param>
        /// <param name="dockPanel"></param>
        public Sketch(System.Windows.Forms.Form Parent, DockPanel dockPanel)
        {
            this._dockPanel = dockPanel;
            this._parent = Parent;
            this._openFileAsyncMethod = new OpenFileAsyncMethod(OpenFile);
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        public frmDocument ActiveDocument
        {
            get
            {
                if (_dockPanel.ActiveDocument is frmDocument)
                    return _dockPanel.ActiveDocument as frmDocument;

                return null;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        public void SetCrrentUploadPort(string value)
        {
            this._crrentUploadPort = value;
            this._compiler.SetPort(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="doc"></param>
        private void SetScintillaToCurrentOptions(frmDocument doc)
        {
            doc.Scintilla.Margins.Margin0.Width = StringConst.LINE_NUMBERS_MARGIN_WIDTH;
            doc.Scintilla.Whitespace.Mode = WhitespaceMode.Invisible;
            doc.Scintilla.LineWrap.Mode = WrapMode.None;

            // Set the zoom
            doc.Scintilla.Zoom = _zoomLevel;
        }

        /// <summary>
        /// 
        /// </summary>
        private void UpdateAllScintillaZoom()
        {
            // Update zoom level for all files
            foreach (var doc in _dockPanel.Documents)
            {
                if (doc is frmDocument)
                    ((frmDocument)doc).Scintilla.Zoom = _zoomLevel;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Title"></param>
        /// <param name="MainInstruction"></param>
        /// <param name="Content"></param>
        private void MessageErrorDialog(string Title, string MainInstruction, string Content)
        {
            PSTaskDialog.cTaskDialog.MessageBox(
                            Title,
                            MainInstruction,
                            Content,
                            eTaskDialogButtons.OK,
                            eSysIcons.Error);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="project"></param>
        private void SetCurrentProject(ProjectEntity project)
        {
            this._curProject = project;

            this.DoProjectSelected(project);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="curProject"></param>
        private void DoProjectSelected(ProjectEntity curProject)
        {
            if (this.OnProjectSelected != null)
                this.OnProjectSelected(this, new ProjectSelectedArgs(curProject));
        }
 
        #region File Menu

        /// <summary>
        /// 
        /// </summary>
        private void NewDocument()
        {
            try
            {
                frmDocument doc = new frmDocument();
                this.SetScintillaToCurrentOptions(doc);
                doc.TabText = String.Format(CultureInfo.CurrentCulture, "{0}{1}", StringConst.NEW_DOCUMENT_TEXT, ++_newDocumentCount);
                doc.Show(_dockPanel); 
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        private void OpenFile()
        {
            this._openFileDialog.Filter = "Arduino Sketch File|*.pde| C++ Source File|*.cpp| C Source File|*.c| Header File|*.h|All File|*.*";
            if (this._openFileDialog.ShowDialog() != DialogResult.OK)
                return;

            foreach (string filePath in _openFileDialog.FileNames)
            {
                this.OpenFile(filePath); //It Ensures that the file is not already open
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        private void OpenFile(string filePath)
        {
            try
            {
                // Ensure this file isn't already open
                foreach (var documentForm in _dockPanel.Documents)
                {
                    if (documentForm is frmDocument)
                    {
                        if (filePath.Equals(((frmDocument)documentForm).ProjectFile.FilePath, StringComparison.OrdinalIgnoreCase))
                        {
                            ((frmDocument)documentForm).Activate();
                            ((frmDocument)documentForm).Select();
                            return;
                        }
                    }
                }

                frmDocument doc = new frmDocument();
                this.SetScintillaToCurrentOptions(doc);

                Encoding _encoding = Encoding.GetEncoding("TIS-620");
                string strFileValue = "";
                using (Stream fsr = new FileStream(filePath, FileMode.Open))
                {
                    using (StreamReader sr = new StreamReader(fsr, _encoding))
                    {
                        strFileValue = sr.ReadToEnd();
                    }
                }

                doc.Scintilla.Text = strFileValue;
                doc.Scintilla.UndoRedo.EmptyUndoBuffer();
                doc.Scintilla.Modified = false;
                doc.TabText = Path.GetFileName(filePath);

                if (this.FileExitsProject(doc.TabText))
                    doc.ProjectFile.Project = _curProject;
                else
                    doc.ProjectFile.Project = null;

                doc.ProjectFile.FilePath = filePath;
                doc.ProjectFile.FileName = Path.GetFileName(filePath);
                doc.Show(_dockPanel);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            } 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool FileExitsProject(string fileName)
        {
            bool result = false;

            if (_curProject != null)
            {
                ProjectFileEntity _file = this._curProject.ProjectFiles.SingleOrDefault(e => e.FileName.Equals(fileName));
                result = _file != null;
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool FileExitsSolution(string fileName)
        {
            bool result = false;

            if (this._curSolution != null)
            {
                this._curSolution.Projects.ForEach(e =>
                    {
                        e.ProjectFiles.ForEach(x =>
                            {
                                if (x.FileName == fileName)
                                {
                                    result = true;
                                    return;
                                }
                            });
                    });
            }

            return result;
        }

        #endregion

        #region Solution Explorer

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _explorer_OnExplorerSelected(object sender, ProjectFileArgs e)
        {
            ProjectFileEntity _projectFile = e.File;
            string _fileName = _projectFile.FileName;
            string _fullPath = string.Format(@"{0}\{1}", _projectFile.FilePath, _fileName);

            if (File.Exists(_fullPath))
            {
                this.SetCurrentProject(e.File.Project);
                foreach (var documentForm in _dockPanel.Documents)
                {
                    if (documentForm is frmDocument)
                    {
                        if (_fullPath.Equals(((frmDocument)documentForm).ProjectFile.FilePath, StringComparison.OrdinalIgnoreCase))
                        {
                            ((frmDocument)documentForm).Activate();

                            ((frmDocument)documentForm).Select();
                            return;
                        }
                    }
                }

                if (this._parent != null)
                    this._parent.BeginInvoke(_openFileAsyncMethod, new object[] { _fullPath });
                else
                    this._openFileAsyncMethod.Invoke(_fullPath); 
            }
            else
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", "File not found");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _explorer_OnProjectSelected(object sender, ProjectSelectedArgs e)
        {
            this.SetCurrentProject(e.Project);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Explorer"></param>
        public void AttachSolutionExplorer(ISolutionExplorer Explorer)
        {
            this._explorer = Explorer;
            this._explorer.OnExplorerSelected += new EventHandler<ProjectFileArgs>(_explorer_OnExplorerSelected);
            this._explorer.OnProjectSelected += new EventHandler<ProjectSelectedArgs>(_explorer_OnProjectSelected);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="console"></param>
        public void AttachOutputConsole(IOutputConsole console)
        {
            this._console = console;
            if (this._compiler == null)
            {
                this._compiler = new Compiler(_console);
                this._compileProjectAsyncMethod = new CompileProjectAsyncMethod(this._compiler.Compile);
                this._uploadAsyncMethod = new UploadAsyncMethod(this._compiler.Upload);

                this._compileCallback = new AsyncCallback(CompileCalback);
                this._uploadCallback = new AsyncCallback(UploadCalback);
            }
        }

        #endregion

        #region File Menu Handlers

        /// <summary>
        /// 
        /// </summary>
        public void New()
        {
            this.NewDocument();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Open()
        {
            this.OpenFile();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Save()
        {
            if (this.ActiveDocument == null)
                return;

            if (String.IsNullOrEmpty(ActiveDocument.ProjectFile.FilePath))
            {
                this.SaveAs();
                return;
            }
            this.Save(ActiveDocument.ProjectFile.FilePath);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        public void Save(string filePath)
        {
            try
            {
                using (FileStream fs = File.Create(filePath))
                {
                    Encoding _encoding = Encoding.GetEncoding("TIS-620");
                    using (BinaryWriter bw = new BinaryWriter(fs, _encoding))
                    {
                        string _lineFeed = ActiveDocument.Scintilla.Lines[ActiveDocument.Scintilla.Lines.Count - 1].Text;
                        if (_lineFeed != String.Empty)
                            this.ActiveDocument.Scintilla.Text += "\r\n";
                        byte[] valueByte = _encoding.GetBytes(ActiveDocument.Scintilla.Text);
                        bw.Write(valueByte, 0, valueByte.Length);
                    }
                }

                //using (Stream fsr = new FileStream(filePath, FileMode.Open))
                //{
                //    using (StreamWriter sw = new StreamWriter(fsr))
                //    {
                //        sw.Write(ActiveDocument.Scintilla.Text);
                //        sw.Flush();
                //        sw.Close();
                //    }
                //}

                // Add to current project
                FileInfo _fileInfo = new FileInfo(filePath);
                if (!File.Exists(_fileInfo.FullName))
                {
                    if ((_curSolution != null) && (_curProject != null))
                    {
                        ProjectFileEntity _projectFile = new ProjectFileEntity();
                        _projectFile.FileName = _fileInfo.Name;
                        _projectFile.FilePath = _fileInfo.DirectoryName;
                        this._curProject.ProjectFiles.Add(_projectFile);

                        this._explorer.CreateFileView(_projectFile);
                    }
                }

                this.ActiveDocument.Scintilla.Modified = false;
                this.ActiveDocument.TabText = _fileInfo.Name;
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        public void SaveAs()
        {
            if (this._saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.ActiveDocument.ProjectFile.FilePath = _saveFileDialog.FileName;
                this.Save(ActiveDocument.ProjectFile.FilePath);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void SaveAll()
        {
            if (this.ActiveDocument == null)
                return;

            foreach (var doc in _dockPanel.Documents)
            {
                if (doc is frmDocument)
                {
                    ((frmDocument)doc).Activate();
                    this.Save(((frmDocument)doc).ProjectFile.FilePath);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Print()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        private void PrintPreview()
        {

        }

        #endregion

        #region Edit Menu Handlers
        /// <summary>
        /// 
        /// </summary>
        public void Undo()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.UndoRedo.Undo();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Redo()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.UndoRedo.Redo();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Cut()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.Clipboard.Cut();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Coppy()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.Clipboard.Copy();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Paste()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.Clipboard.Paste();
        }
        /// <summary>
        /// 
        /// </summary>
        public void SelectAll()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.Selection.SelectAll();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Find()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.FindReplace.ShowFind();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Replace()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.FindReplace.ShowReplace();
        }
        /// <summary>
        /// 
        /// </summary>
        public void GoToLine()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.GoTo.ShowGoToDialog();
        }
        /// <summary>
        /// 
        /// </summary>
        public void ToggleBookmark()
        {
            if (this.ActiveDocument == null)
                return;

            Line currentLine = this.ActiveDocument.Scintilla.Lines.Current;
            if (this.ActiveDocument.Scintilla.Markers.GetMarkerMask(currentLine) == 0)
            {
                currentLine.AddMarker(0);
            }
            else
            {
                currentLine.DeleteMarker(0);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public void PreviosBookmark()
        {
            if (this.ActiveDocument == null)
                return;

            Line l = this.ActiveDocument.Scintilla.Lines.Current.FindPreviousMarker(1);
            if (l != null)
                l.Goto();
        }
        /// <summary>
        /// 
        /// </summary>
        public void NextBookmark()
        {
            if (ActiveDocument == null)
                return;

            Line l = this.ActiveDocument.Scintilla.Lines.Current.FindNextMarker(1);
            if (l != null)
                l.Goto();
        }
        /// <summary>
        /// 
        /// </summary>
        public void ClearBookmars()
        {
            if (this.ActiveDocument != null)
                this.ActiveDocument.Scintilla.Markers.DeleteAll(0);
        }

        #endregion

        #region Sketch Menu Helper

        #region New Sketch

        /// <summary>
        /// 
        /// </summary>
        public void NewSketch()
        {
            this.DoNewSketch();           
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        private ProjectEntity CreateProject(SolutionEntity solution, string ProfileName)
        {
            ProjectEntity _project = new ProjectEntity()
            {
                ProjectName = solution.SolutionName,
                ProjectPath = solution.SolutionPath,
                HardwareProfile = ProfileName
            };
            this.SetCurrentProject(_project);
            string _pdeFileName = string.Format("{0}.pde", _project.ProjectName);
            string _pdeFullPath = string.Format(@"{0}\{1}", _project.ProjectPath, _pdeFileName);

            _project.ProjectFiles.Add(new ProjectFileEntity()
            {
                FileName = _pdeFileName,
                FilePath = _project.ProjectPath
            });
            _curSolution.Projects.Add(_project);

            using (StreamWriter writer = new StreamWriter(_pdeFullPath, false))
            {
                writer.Write(StringConst.DefaultPdeFile());
                writer.Flush();
                writer.Close();
            }
            this.OpenFile(_pdeFullPath);
            this.SetCurrentProject(_project);
            return _project;
        }

        /// <summary>
        /// 
        /// </summary>
        private void DoNewSketch()
        {
            frmNewSketch _newSketch = new frmNewSketch();
            string _fileName = String.Empty;
            try
            {
                if (_newSketch.ShowDialog() == DialogResult.OK)
                {
                    if (!Directory.Exists(_newSketch.Solution.SolutionPath))
                    {
                        if (this._curSolution == null)
                        {
                            this._curSolution = _newSketch.Solution;
                            Directory.CreateDirectory(_curSolution.SolutionPath);

                            this.CreateProject(_curSolution, _newSketch.HardwareProfile);
                            this._explorer.CreateSolutionView(_curSolution);
                        }
                        else
                        {
                            Directory.CreateDirectory(_newSketch.Solution.SolutionPath);
                            ProjectEntity _project = this.CreateProject(_newSketch.Solution, _newSketch.HardwareProfile);
                            this._explorer.CreateProjectView(_project);
                        }
                        _fileName = string.Format(@"{0}\{1}.skt", _curSolution.SolutionPath, _curSolution.SolutionName);
                        XmlSerializable.WriteFile(_curSolution, _fileName);
                    }
                    else
                    {
                        PSTaskDialog.cTaskDialog.MessageBox(
                            "Error",
                            "Create Solution",
                            "Project or solution already exists",
                            eTaskDialogButtons.OK,
                            eSysIcons.Error);
                    }
                }

            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            finally
            {
                _newSketch.Dispose();
                _newSketch = null;
            }
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        public void AddFileToSketch(string fileName)
        {
            try
            {
                FileInfo _fileInfo = new FileInfo(fileName);
                string _destFile = String.Format(@"{0}\{1}", this._curProject.ProjectPath, _fileInfo.Name);

                if (!File.Exists(_destFile))
                    File.Copy(_fileInfo.FullName, _destFile);

                _fileInfo = new FileInfo(_destFile);
                ProjectFileEntity _projectFileEntity = new ProjectFileEntity()
                {
                    FileName = _fileInfo.Name,
                    FilePath = _fileInfo.DirectoryName,
                    Project = _curProject
                };
                this._curProject.ProjectFiles.Add(_projectFileEntity);
                this.SaveSketch();
                this._explorer.CreateFileView(_projectFileEntity);
                this._parent.BeginInvoke(_openFileAsyncMethod, new object[] { _destFile });
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newFileType"></param>
        public void NewFileToSketch(NewFileType newFileType)
        {
            NewFileDialog _newDialog = new NewFileDialog(newFileType, _curProject.ProjectName);
            try
            {
                if (_newDialog.ShowDialog() == DialogResult.OK)
                {
                    string filePath = String.Format(@"{0}\{1}", _curProject.ProjectPath, _newDialog.NewFileName);
                    File.Create(filePath);
                    FileInfo _fileInfo = new FileInfo(filePath);
                    ProjectFileEntity _projectFileEntity = new ProjectFileEntity()
                    {
                        FileName = _fileInfo.Name,
                        FilePath = _fileInfo.DirectoryName,
                        Project = _curProject
                    };
                    this._curProject.ProjectFiles.Add(_projectFileEntity);
                    this.SaveSketch();
                    this._explorer.CreateFileView(_projectFileEntity);
                    this.OpenFile(filePath);
                    //this._parent.BeginInvoke(_openFileAsyncMethod, new object[] { filePath });
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            finally
            {
                _newDialog.Dispose();
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        public void RemoveFileFormSketch(string fileName)
        {
            if (_curProject != null)
            {
                try
                {
                    ProjectFileEntity _objFile = _curProject.ProjectFiles.SingleOrDefault(e => e.FileName.Equals(fileName));
                    if (_objFile != null)
                    {
                        DialogResult _dialogResult = PSTaskDialog.cTaskDialog.MessageBox(
                                "Question",
                                "Delete Resources",
                                String.Format("Are you sure you want to delete \"{0}\" form the file system", fileName),
                                eTaskDialogButtons.OKCancel,
                                eSysIcons.Question);
                        if (_dialogResult == DialogResult.OK)
                        {
                            string _fileName = String.Format(@"{0}\{1}", _objFile.FilePath, _objFile.FileName);
                            _curProject.ProjectFiles.Remove(_objFile);
                            File.Delete(_fileName);
                            this._explorer.RemoveSelectedNode();
                            foreach (var documentForm in _dockPanel.Documents)
                            {
                                if (documentForm is frmDocument)
                                {
                                    if (fileName.Equals(((frmDocument)documentForm).ProjectFile.FileName, StringComparison.OrdinalIgnoreCase))
                                    {
                                        ((frmDocument)documentForm).Close();
                                        break;
                                    }
                                }
                            }

                            this.SaveSketch();
                        }
                    }
                }
                catch (Exception ex)
                {
                    this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
                }
                
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void AddExistingProject()
        {
            this._openFileDialog.Filter = "Arduino Sketch File|*.pde";

            try
            {
                if (_openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    FileInfo _fileInfo = new FileInfo(_openFileDialog.FileName);
                    string _projectName = _fileInfo.Name.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries)[0];
                    // Check Project Exist
                    ProjectEntity _objProject = _curSolution.Projects.SingleOrDefault(e => e.ProjectName.Equals(_projectName));
                    //
                    if (_objProject == null)
                    {
                        _objProject = new ProjectEntity() { ProjectName = _projectName, ProjectPath = _fileInfo.DirectoryName };
                        this._curSolution.Projects.Add(_objProject);
                        DirectoryInfo _dirInfo = new DirectoryInfo(_fileInfo.DirectoryName);
                        FileInfo[] _fileInfos = _dirInfo.GetFiles();
                        foreach (var _file in _fileInfos)
                        {
                            if ((_file.Extension.Equals(".cpp")) ||
                                _file.Extension.Equals(".c") ||
                                _file.Extension.Equals(".h") ||
                                _file.Extension.Equals(".pde"))
                            {
                                ProjectFileEntity _projectFile = new ProjectFileEntity()
                                {
                                    FileName = _file.Name,
                                    FilePath = _file.DirectoryName,
                                    Project = _objProject
                                };
                                _objProject.ProjectFiles.Add(_projectFile);
                            }
                        }
                        this.SaveSketch();
                        this._explorer.CreateProjectView(_objProject);
                    }
                    else
                    {
                        PSTaskDialog.cTaskDialog.MessageBox(
                                "Error",
                                "Add Existing Project",
                                String.Format("Project \"{}\" already exist", _projectName),
                                eTaskDialogButtons.OK,
                                eSysIcons.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        public void OpenSketch()
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    this.CloseAllDocument();

                    DirectoryInfo _directoryInfo = new DirectoryInfo(folderBrowserDialog.SelectedPath);
                    FileInfo[] fileInfos = _directoryInfo.GetFiles();
                    bool _validate = false;
                    string _sketchFile = String.Empty;

                    foreach (var fInfo in fileInfos)
                    {
                        if (fInfo.Extension.Equals(".skt"))
                        {
                            _validate = true;
                            _sketchFile = fInfo.FullName;
                        }
                    }
                    if (_validate)
                    {
                        this._curSolution = XmlSerializable.ReadFile<SolutionEntity>(_sketchFile);
                        if (this._curSolution != null)
                        {
                            // Retarget
                            FileInfo _targetInfo = new FileInfo(_sketchFile);
                            this._curSolution.SolutionPath = _targetInfo.DirectoryName;
                            this.SaveSketch();
                            //
                            this._explorer.CreateSolutionView(_curSolution);
                            if (this._curSolution.Projects.Count > 0)
                                this.SetCurrentProject(_curSolution.Projects[0]);
                        }
                    }
                    else
                    {
                        this.MessageErrorDialog("Error", "Open Solution", "Project or solution not exists");
                    }
                }
                catch (Exception ex)
                {
                    this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
                } 
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void CloseAllDocument()
        {
            List<frmDocument> _docs = new List<frmDocument>();
            foreach (var doc in _dockPanel.Documents)
            {
                if (doc is frmDocument)
                    _docs.Add((frmDocument)doc);
            }
            _docs.ForEach(delegate(frmDocument d) { d.Close(); });
        }

        /// <summary>
        /// 
        /// </summary>
        public void SaveSketch()
        {
            if (_curSolution != null)
            {
                string _fileName = string.Format(@"{0}/{1}.skt", _curSolution.SolutionPath, _curSolution.SolutionName);
                this.SaveSketch(_fileName);
            }
        }
            
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sketchPath"></param>
        public void SaveSketch(string sketchPath)
        {
            try
            {
                XmlSerializable.WriteFile<SolutionEntity>(_curSolution, sketchPath);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void SaveSketchAs()
        {
            if (_saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                string _fileName = _saveFileDialog.FileName;
                this.SaveSketch(_fileName);
            }
        }

        private void CompileCalback(IAsyncResult result)
        {
            CompileProjectAsyncMethod _asyncMethod = (CompileProjectAsyncMethod)((AsyncResult)result).AsyncDelegate;
            _asyncMethod.EndInvoke(result);

            if (this.OnAfterCompile != null)
                this.OnAfterCompile(this, new EventArgs());
            

        }

        private void UploadCalback(IAsyncResult result)
        {
            UploadAsyncMethod _asyncMethod = (UploadAsyncMethod)((AsyncResult)result).AsyncDelegate;
            _asyncMethod.EndInvoke(result);

            if (this.OnAfterUpload != null)
                this.OnAfterUpload(this, new EventArgs());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool CreateProjectFormFile(string fileName)
        {
            FileInfo _fileInfo = new FileInfo(fileName);
            bool _result = false;

            if (!_fileInfo.Extension.Equals(".pde"))
            {
                PSTaskDialog.cTaskDialog.MessageBox(
                            "Question",
                            "Compile/Verify",
                            "Arduino sketch file not support",
                            eTaskDialogButtons.OK,
                            eSysIcons.Question);
                return _result;
            }

            HardwareProfileDialog _profile = new HardwareProfileDialog();
            if (_profile.ShowDialog() == DialogResult.OK)
            {
                ProjectEntity _project = new ProjectEntity()
                {
                    HardwareProfile = _profile.GetHardwaresProfile.Name,
                    ProjectName = "",
                    ProjectPath = _fileInfo.DirectoryName
                };
                ActiveDocument.ProjectFile.Project = _project;
                ProjectFileEntity _file = new ProjectFileEntity();
                _file.FileName = _fileInfo.Name;
                _file.FilePath = _fileInfo.DirectoryName;
                _file.Project = _project;
                _project.ProjectFiles.Add(_file);

                _result = true;
            }

            return _result;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Compile()
        {
            if (this.ActiveDocument == null)
                return;

            if ((this.ActiveDocument.ProjectFile.Project == null) ||
                (!FileExitsSolution(this.ActiveDocument.ProjectFile.FileName)))
            {
                if (!this.CreateProjectFormFile(this.ActiveDocument.ProjectFile.FilePath))
                    return;
            }

            this.SetCurrentProject(this.ActiveDocument.ProjectFile.Project);
            if (this._curProject != null)
            {
                // Recheck Hard Profile
                if ((string.IsNullOrEmpty(this._curProject.HardwareProfile)) ||
                    (Utils.GetHardwareProfile(this._curProject.HardwareProfile) == null))
                {
                    HardwareProfileDialog _profileDialog = new HardwareProfileDialog();
                    if (_profileDialog.ShowDialog() == DialogResult.Cancel)
                        return;
                    this._curProject.HardwareProfile = _profileDialog.GetHardwaresProfile.Name;
                    this.SaveSketch();
                }
                this.Save();

                if (this.OnBeforCompile != null)
                    this.OnBeforCompile(this, new EventArgs());

                this._compileProjectAsyncMethod.BeginInvoke(_curProject, _compileCallback, null);
            }
            else
            {
                PSTaskDialog.cTaskDialog.MessageBox(
                            "Information",
                            "Compile/Verify",
                            "Please select project befor Compile/Verify",
                            eTaskDialogButtons.OK,
                            eSysIcons.Information);
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        public void Upload()
        {
            if (this.ActiveDocument == null)
                return;

            if (String.IsNullOrEmpty(this._crrentUploadPort))
            {
                PSTaskDialog.cTaskDialog.MessageBox(
                            "Error",
                            "Upload",
                            "Please select port befor upload",
                            eTaskDialogButtons.OK,
                            eSysIcons.Information);
                return;
            }

            if ((this.ActiveDocument.ProjectFile.Project == null) ||
                (!FileExitsSolution(this.ActiveDocument.ProjectFile.FileName)))
            {
                if (!this.CreateProjectFormFile(this.ActiveDocument.ProjectFile.FilePath))
                    return;
            }

            this.SetCurrentProject(this.ActiveDocument.ProjectFile.Project);
            if (this._curProject != null)
            {
                this.Save();

                if (this.OnBeforUpload != null)
                    this.OnBeforUpload(this, new EventArgs());

                this._uploadAsyncMethod.BeginInvoke(_curProject, _uploadCallback, null);
            }
            else
            {
                PSTaskDialog.cTaskDialog.MessageBox(
                            "Information",
                            "Upload",
                            "Please select project befor Upload",
                            eTaskDialogButtons.OK,
                            eSysIcons.Information);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void RemoveSketch()
        {
            if (this._curProject == null)
                return;

            DialogResult _dialogResult = PSTaskDialog.cTaskDialog.MessageBox(
                            "Information",
                            "Delete Resource",
                            string.Format("Are you sure you want to delete \"{0}\" form the file system", _curProject.ProjectName),
                            "",
                            "After delete cannot be undone",
                            "Delete project contents on disk",
                            eTaskDialogButtons.OKCancel,
                            eSysIcons.Information,
                            PSTaskDialog.eSysIcons.Warning);
            if (_dialogResult == DialogResult.OK)
            {
                // Delete project contents on disk
                if (PSTaskDialog.cTaskDialog.VerificationChecked)
                {
                    DirectoryInfo _dirInfo = new DirectoryInfo(_curProject.ProjectPath);
                    FileInfo[] _fileInfos = _dirInfo.GetFiles();
                    foreach (var _file in _fileInfos)
                    {
                        File.Delete(_file.FullName);
                    }
                    Directory.Delete(_curProject.ProjectPath);
                } 

                ProjectEntity _project = this._curSolution.Projects.SingleOrDefault(e => e.ProjectName.Equals(_curProject.ProjectName));
                if (_project != null)
                {
                    this._curSolution.Projects.Remove(_project);
                    this.SaveSketch();
                }

                // Close document
                List<frmDocument> _closeDoc = new List<frmDocument>();
                foreach (var doc in _dockPanel.Documents)
                {
                    if (doc is frmDocument)
                    {
                        foreach (var _file in _curProject.ProjectFiles)
                        {
                            if (_file.FileName.Equals(((frmDocument)doc).ProjectFile.FileName))
                            {
                                _closeDoc.Add((frmDocument)doc);
                            }
                        }
                    }
                }
                this._curProject = null;
                _closeDoc.ForEach(e => { e.Close(); });
                this._explorer.RemoveSelectedNode();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void SketchProperies()
        {
            if (this.ActiveDocument == null)
                return;

            if (_curProject != null)
            {
                SketchProperties _properties = new SketchProperties();
                _properties.ShowDialog(this._curProject);
                _properties.Dispose();

                this.DoProjectSelected(this._curProject);
                this.SaveSketch();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void CloseSolution()
        {
            DialogResult _result = PSTaskDialog.cTaskDialog.MessageBox(
                                    "Question",
                                    "Solution",
                                    "Are you sure you want to close all project?",
                                    eTaskDialogButtons.OKCancel,
                                    eSysIcons.Question);
            if (_result == DialogResult.OK)
            {
                this.SaveSketch();
                this.CloseAllDocument();
                this._curProject = null;
                this._curSolution = null;
                this._explorer.ClearSolution();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void UpdateLibrary()
        {
            PSTaskDialog.cTaskDialog.MessageBox(
                            "Information",
                            "Library",
                            "Update library complate",
                            eTaskDialogButtons.OK,
                            eSysIcons.Information);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Lib"></param>
        public void AddLibrary(Library Lib)
        {
            string _includeLib;

            if (ActiveDocument == null)
                return;

            foreach (var lib in Lib.LibraryNames)
            {
                _includeLib = string.Format("#include <{0}>", lib);
                this.ActiveDocument.Scintilla.InsertText(0, "\r\n");
                this.ActiveDocument.Scintilla.InsertText(0, _includeLib);
            }
        }

        #endregion

        #region View

        public void ZoomIn()
        {
            _zoomLevel++;
            
            this.UpdateAllScintillaZoom();
        }

        public void ZoomOut()
        {
            _zoomLevel--;
            this.UpdateAllScintillaZoom();
        }

        #endregion

    }
}
